namespace Angelus.Data.Linq.ProviderBase
{   
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
	using System.Data.DLinq;
	using System.Data.DLinq.ProviderBase;
    using System.Expressions;

    /// <summary>
    /// 
    /// </summary>
    internal class OracleParameterizer
    {
        #region Fields

        private readonly Dictionary<SqlParameterInfo, bool> _parameters;
        private readonly SqlEval _eval;
        private readonly Dictionary<object, SqlParameterInfo> _map;
        private readonly List<ReadOnlyCollection<SqlParameterInfo>> _queryParameters;

        #endregion

        #region Constructors

        /// <summary>
        /// 
        /// </summary>
        /// <param name="eval"></param>
        internal OracleParameterizer(SqlEval eval)
        {
            this._map = new Dictionary<object, SqlParameterInfo>();
            this._queryParameters = new List<ReadOnlyCollection<SqlParameterInfo>>();
            this._parameters = new Dictionary<SqlParameterInfo, bool>();
            this._eval = eval;
        }

        #endregion

        #region Parameters Implementation

        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        internal virtual string CreateParameterName(int index)
        {
            return string.Format(":p{0}", index);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        internal ReadOnlyCollection<ReadOnlyCollection<SqlParameterInfo>> GetParameters()
        {
            return this._queryParameters.ToReadOnlyCollection();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="unary"></param>
        /// <returns></returns>
        private SqlParameter InsertLookup(SqlUnary unary)
        {
            SqlParameterInfo parameterInfo;
            if (!this._map.ContainsKey(unary))
            {
                parameterInfo = new SqlParameterInfo();
                parameterInfo.Expression = this._eval.Visit(unary.Operand);
                parameterInfo.Parameter = new SqlParameter(unary.ClrType, unary.SqlType, this.CreateParameterName(this._map.Count),
                                                   unary.SourceExpression);
                this._map.Add(unary, parameterInfo);
                this._parameters.Add(parameterInfo, true);
            }
            else
            {
                parameterInfo = this._map[unary];
            }
            if (!this._parameters.ContainsKey(parameterInfo))
            {
                this._parameters.Add(parameterInfo, true);
            }
            return parameterInfo.Parameter;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="cursor"></param>
        private SqlParameter InsertRefCursor(OracleRefCursor cursor)
        {
            SqlParameterInfo parameterInfo;
            if (!this._map.ContainsKey(cursor))
            {
                parameterInfo = new SqlParameterInfo();
                parameterInfo.Expression = null;
                parameterInfo.Parameter = new SqlParameter(cursor.Parameter.ClrType, cursor.Parameter.SqlType,
                                                  this.CreateParameterName(this._map.Count), cursor.SourceExpression);
                this._map.Add(cursor, parameterInfo);
                this._parameters.Add(parameterInfo, true);
            }
            else
            {
                parameterInfo = this._map[cursor];
            }
            if (!this._parameters.ContainsKey(parameterInfo))
            {
                this._parameters.Add(parameterInfo, true);
            }
            return parameterInfo.Parameter;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="node"></param>
        internal void Parameterize(SqlNode node)
        {
            new Visitor(this).Visit(node);
            if (this._queryParameters.Count == 0)
            {
                this._queryParameters.Add(this._parameters.Keys.ToReadOnlyCollection());
            }
        }

        #endregion

        #region Nested classes

        // Nested Types
        private class Visitor : SqlVisitor
        {
            // Methods
            private readonly OracleParameterizer _parameterizer;
            private bool _topLevel;

            internal Visitor(OracleParameterizer parameterizer)
            {
                this._parameterizer = parameterizer;
                this._topLevel = true;
            }

            internal override SqlExpression VisitClientExpression(SqlUnary u)
            {
                if (this._topLevel)
                {
                    return u;
                }
                return this._parameterizer.InsertLookup(u);
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="c"></param>
            /// <returns></returns>
            internal override SqlStatement VisitCursor(SqlCursor c)
            {
                Visit(c.Select);
                OracleRefCursor cursor = (OracleRefCursor)c;
                cursor.Parameter = this._parameterizer.InsertRefCursor(c as OracleRefCursor);
                return c;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="sin"></param>
            /// <returns></returns>
            internal override SqlStatement VisitInsert(SqlInsert sin)
            {
                bool flag2 = this._topLevel;
                this._topLevel = false;
                base.VisitInsert(sin);
                this._topLevel = flag2;
                return sin;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="qs"></param>
            /// <returns></returns>
            internal override SqlQuerySet VisitQuerySet(SqlQuerySet qs)
            {
                //int num1 = 0;
                //int num2 = qs.Queries.Count;
                //while (num1 < num2)
                //{
                //    this._parameterizer._parameters.Clear();
                //    this._topLevel = true;
                //    qs.Queries[num1] = Visit(qs.Queries[num1]);
                //    this._parameterizer._queryParameters.Add(this._parameterizer._parameters.Keys.ToReadOnlyCollection());
                //    num1++;
                //}
                for (int index = 0, max = qs.Queries.Count; index < max; index++)
                {
                    this._parameterizer._parameters.Clear();
                    this._topLevel = true;
                    qs.Queries[index] = Visit(qs.Queries[index]);
                    this._parameterizer._queryParameters.Add(this._parameterizer._parameters.Keys.ToReadOnlyCollection());
                }
                return qs;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="select"></param>
            /// <returns></returns>
            internal override SqlSelect VisitSelect(SqlSelect select)
            {
                bool flag2 = this._topLevel;
                this._topLevel = false;
                select.From = (SqlSource) Visit(select.From);
                select.Where = VisitExpression(select.Where);
                //int num1 = 0;
                //int num2 = select.GroupBy.Count;
                //while (num1 < num2)
                //{
                //    select.GroupBy[num1] = VisitExpression(select.GroupBy[num1]);
                //    num1++;
                //}
                for (int index = 0, max = select.GroupBy.Count; index < max; index++)
                {
                    select.GroupBy[index] = VisitExpression(select.GroupBy[index]);
                }

                select.Having = VisitExpression(select.Having);
                //int num3 = 0;
                //int num4 = select.OrderBy.Count;
                //while (num3 < num4)
                //{
                //    select.OrderBy[num3].Expression = VisitExpression(select.OrderBy[num3].Expression);
                //    num3++;
                //}
                for (int index = 0, max = select.OrderBy.Count; index < max; index++)
                {
                    select.OrderBy[index].Expression = VisitExpression(select.OrderBy[index].Expression);
                }

                select.Top = VisitExpression(select.Top);
                select.Row = (SqlRow) Visit(select.Row);
                this._topLevel = flag2;
                select.Selection = VisitExpression(select.Selection);
                return select;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="sup"></param>
            /// <returns></returns>
            internal override SqlStatement VisitUpdate(SqlUpdate sup)
            {
                bool flag2 = this._topLevel;
                this._topLevel = false;
                base.VisitUpdate(sup);
                this._topLevel = flag2;
                return sup;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="suq"></param>
            /// <returns></returns>
            internal override SqlUserQuery VisitUserQuery(SqlUserQuery suq)
            {
                bool flag2 = this._topLevel;
                this._topLevel = false;
                //int num1 = 0;
                //int num2 = suq.Args.Count;
                //while (num1 < num2)
                //{
                //    suq.Args[num1] = VisitExpression(suq.Args[num1]);
                //    num1++;
                //}
                for (int index = 0, max = suq.Args.Count; index < max; index++)
                {
                    suq.Args[index] = VisitExpression(suq.Args[index]);
                }
                this._topLevel = flag2;
                suq.Projection = VisitExpression(suq.Projection);
                return suq;
            }
        }

        #endregion
    }
}